package com.yami.shop.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yami.shop.bean.enums.TakeStockBillStatusEnum;
import com.yami.shop.bean.enums.TakeStockExceptionEnum;
import com.yami.shop.bean.enums.TakeStockProdStatusEnum;
import com.yami.shop.bean.model.Product;
import com.yami.shop.bean.model.Sku;
import com.yami.shop.bean.model.TakeStock;
import com.yami.shop.bean.model.TakeStockProd;
import com.yami.shop.bean.vo.TakeStockProdVO;
import com.yami.shop.bean.vo.TakeStockVO;
import com.yami.shop.common.config.Constant;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.i18n.I18nMessage;
import com.yami.shop.common.i18n.LanguageEnum;
import com.yami.shop.common.util.PoiExcelUtil;
import com.yami.shop.dao.ProductMapper;
import com.yami.shop.dao.TakeStockMapper;
import com.yami.shop.service.SkuService;
import com.yami.shop.service.TakeStockExcelService;
import com.yami.shop.service.TakeStockProdService;
import com.yami.shop.service.TakeStockService;
import ma.glasnost.orika.MapperFacade;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author 菠萝凤梨
 * @date 2022/4/12 16:26
 */
@Service
public class TakeStockExcelServiceImpl extends ServiceImpl<TakeStockMapper, TakeStock> implements TakeStockExcelService {

    @Autowired
    private TakeStockService takeStockService;
    @Autowired
    private TakeStockProdService takeStockProdService;
    @Autowired
    private SkuService skuService;
    @Autowired
    private MapperFacade mapperFacade;
    @Autowired
    private ProductMapper productMapper;

    @Override
    public void downLoadModel(HttpServletResponse response) {
        createExcel(null, true, response);
    }

    @Override
    public Object parseFile(MultipartFile file, Long shopId, Long takeStockId) throws Exception {
        Integer lang = I18nMessage.getDbLang();
        TakeStock takeStock = takeStockService.getById(takeStockId);
        if (Objects.equals(takeStock.getBillStatus(), TakeStockBillStatusEnum.VOIDED.value()) ||
                Objects.equals(takeStock.getBillStatus(), TakeStockBillStatusEnum.COMPLETED.value())) {
            //当前盘点任务已完成或已作废，请返回列表页面后刷新
            throw new YamiShopBindException("yami.take.stock.status.error");
        }
        if (CollectionUtil.isNotEmpty(takeStock.getTakeStockProdList())) {
            //该盘点单已有商品导入，无法使用批量导入功能
            throw new YamiShopBindException("yami.take.stock.import.error");
        }
        try (InputStream inputStream = file.getInputStream();
             Workbook workbook = WorkbookFactory.create(inputStream)) {
            Sheet sheet = workbook.getSheetAt(0);
            int rowNumber = sheet.getPhysicalNumberOfRows();
            if (rowNumber <= Constant.EXCEL_BEGIN_ROW) {
                // 文件无内容
                throw new YamiShopBindException("yami.document.no.content");
            }
            if (!(file.getOriginalFilename().contains(Constant.XLS_FILE) || file.getOriginalFilename().contains(Constant.XLSX_FILE))) {
                // 格式错误
                throw new YamiShopBindException("yami.file.error");
            }
            //商品编码对应的skuId以及prodId
            List<Product> products = productMapper.selectList(new LambdaQueryWrapper<Product>().eq(Product::getShopId, shopId)
                    .ne(Product::getStatus, -1));
            List<Long> prodIds = products.stream().map(Product::getProdId).collect(Collectors.toList());
            List<String> partyCodes = skuService.listSkuByProdIds(prodIds, null);
            List<TakeStockProd> takeStockProds = new ArrayList<>();
            List<String> errorList = new ArrayList<>();
            String response;
            int insertRows = 0;
            for (int i = 2; i < rowNumber; i++) {
                try {
                    Row row = sheet.getRow(i);
                    int cell = 0;
                    //{"商品编码（必填*）", "商品名称", "商品规格", "盘点数量（必填*）"}
                    String partyCode = PoiExcelUtil.getRowValue(row, cell, false);
                    String prodName = PoiExcelUtil.getRowValue(row, ++cell, false);
                    String skuName = PoiExcelUtil.getRowValue(row, ++cell, false);
                    String stock = PoiExcelUtil.getRowValue(row, ++cell, true);
                    insertRows++;
                    List<Sku> skus;
                    if (StrUtil.isBlank(partyCode) || !partyCodes.contains(partyCode)) {
                        //商品编码不存在
                        throw new YamiShopBindException("yami.sku.party.code.not.exist");
                    }
                    skus = skuService.list(new LambdaQueryWrapper<Sku>().eq(Sku::getPartyCode, partyCode));
                    if (skus.size() > 1) {
                        //该商品编码下存在多个商品规格，请更新对应的商品规格编码后重新导入
                        throw new YamiShopBindException("yami.sku.party.code.is.repeated");
                    }
                    if (StrUtil.isBlank(stock)) {
                        stock = "0";
                    }
                    Integer totalStock = Integer.parseInt(stock);
                    TakeStockProd takeStockProd = getTakeStockProd(takeStockId, skus, totalStock);
                    takeStockProds.add(takeStockProd);
                } catch (Exception e) {
                    errorList.add("第" + (i + 1) + "行数据有错误：" + e.getMessage());
                }
            }
            //商品不为空批量导入
            if (CollectionUtil.isNotEmpty(takeStockProds)) {
                takeStock.setTakeStockProdList(takeStockProds);
                takeStockService.updateTakeStock(takeStock);
                response = "检查到" + insertRows + "条盘点商品信息，成功导入" +
                        takeStockProds.size() + "条盘点商品信息！错误" + errorList.size() + "条盘点商品信息！" +
                        (CollUtil.isNotEmpty(errorList) ? errorList.toString() : "");
                return response;
            }
            response = Objects.equals(lang, LanguageEnum.LANGUAGE_ZH_CN.getLang()) ? "数据错误!导入0条" : "Data error! Import 0 items";
            if (CollUtil.isNotEmpty(errorList)) {
                response = response + errorList.toString();
            }
            return response;
        } catch (Exception e) {
            return e.getMessage();
        }
    }

    @NotNull
    private TakeStockProd getTakeStockProd(Long takeStockId,List<Sku> skus, Integer totalStock) {
        TakeStockProd takeStockProd = new TakeStockProd();
        Sku sku = skus.get(0);
        takeStockProd.setProdId(sku.getProdId());
        takeStockProd.setSkuId(sku.getSkuId());
        takeStockProd.setStocks(sku.getStocks());
        takeStockProd.setTakeStockId(takeStockId);
        takeStockProd.setTotalStock(totalStock);
        return takeStockProd;
    }

    @Override
    public void exportTakeStockProd(Long takeStockId, HttpServletResponse response) {
        TakeStock takeStock = takeStockService.getById(takeStockId);
        TakeStockVO takeStockVO = mapperFacade.map(takeStock, TakeStockVO.class);
        List<TakeStockProdVO> stockProdVOList = takeStockProdService.listShopProd(takeStockId);
        takeStockVO.setTakeStockProdList(stockProdVOList);
        ArrayList<TakeStockVO> takeStocks = new ArrayList<>();
        takeStocks.add(takeStockVO);
        createExcel(takeStocks, false, response);
    }

    @Override
    public void createExcel(List<TakeStockVO> takeStocks, Boolean isModel, HttpServletResponse response) {
        ExcelWriter writer = ExcelUtil.getBigWriter();
        List<String> headerList;
        String[] headerCn;
        String[] headerEn;
        if (isModel) {
            //模板
            headerCn = new String[]{"商品编码（必填*）", "商品名称", "商品规格", "盘点数量"};
            headerEn = new String[]{"Party Code（Required*）", "Product Name", "Product Sku", "Number of Inventory（Required*）"};
        } else {
            headerCn = new String[]{"盘点单单号", "盘点日期", "盘点状态", "操作人", "盘点区域名称", "备注", "商品名称", "商品规格",
                    "商品编码", "账面库存", "实盘库存", "盈亏数量", "盈亏状态", "盘点商品备注", "异常原因"};
            headerEn = new String[]{"Inventory list number", "Date of Inventory", "Inventory status", "Operator", "Inventory area name",
                    "Remark", "Product Name", "Product Sku", "Party Code", "Book stock", "Physical Inventory", "Profit & Loss Number",
                    "Profit & Loss Status", "Remarks on Inventory Products", "Reasons for anomalies"};
        }
        headerList = Arrays.asList(Objects.equals(I18nMessage.getDbLang(), 0) ? headerCn : headerEn);
        Sheet sheet = writer.getSheet();
        for (int i = 0; i < headerCn.length-1; i++) {
            sheet.setColumnWidth(i, 20 * 256);
        }
        writer.merge(headerList.size() - 1, Objects.equals(I18nMessage.getDbLang(), 0) ? "盘点商品明细" : "Inventoried Product Information");
        writer.writeRow(headerList);
        int row = 1;
        if (Objects.nonNull(takeStocks)) {
            for (TakeStockVO takeStock : takeStocks) {
                List<TakeStockProdVO> takeStockProdList = takeStock.getTakeStockProdList();
                int firstRow = row + 1;
                int lastRow = row + takeStockProdList.size();
                int col = -1;
                //盘点单单号
                PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, takeStock.getTakeStockNo());
                //盘点日期
                PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, takeStock.getCreateTime());
                //盘点状态
                TakeStockBillStatusEnum billStatus = TakeStockBillStatusEnum.instance(takeStock.getBillStatus());
                PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col,
                        Objects.isNull(billStatus) ? "" : I18nMessage.getDbLang() == 0 ? billStatus.strCn() : billStatus.strEn());
                //操作人
                PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, takeStock.getMakerMobile());
                //盘点区域名称
                PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, takeStock.getStockRegionName());
                //备注
                PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, takeStock.getRemark());
                if (CollectionUtil.isEmpty(takeStockProdList)) {
                    row++;
                    continue;
                }
                for (TakeStockProdVO prod : takeStockProdList) {
                    int index = col;
                    row++;
                    //商品名称
                    writer.writeCellValue(++index, row, prod.getProdName());
                    //商品规格
                    writer.writeCellValue(++index, row, prod.getSkuName());
                    //商品编码
                    writer.writeCellValue(++index, row, prod.getPartyCode());
                    //账面库存
                    writer.writeCellValue(++index, row, prod.getStocks());
                    //实盘库存
                    writer.writeCellValue(++index, row, prod.getTotalStock());
                    //盈亏数量
                    writer.writeCellValue(++index, row, prod.getTotalStock() - prod.getStocks());
                    //盈亏状态
                    TakeStockProdStatusEnum prodStatus = TakeStockProdStatusEnum.instance(prod.getIoType());
                    writer.writeCellValue(++index, row,
                            Objects.isNull(prodStatus) ? "" : I18nMessage.getDbLang() == 0 ? prodStatus.str() : prodStatus.strEn());
                    //盘点商品备注
                    writer.writeCellValue(++index, row, prod.getRemark());
                    //异常原因
                    if (Objects.nonNull(prod.getExceptionReason())) {
                        TakeStockExceptionEnum exceptionEnum = TakeStockExceptionEnum.instance(prod.getExceptionReason());
                        writer.writeCellValue(++index, row,
                                Objects.isNull(exceptionEnum) ? "" : I18nMessage.getDbLang() == 0 ? exceptionEnum.strCn() : exceptionEnum.strEn());
                    }
                }
            }
        }
        PoiExcelUtil.writeExcel(response, writer);
    }
}
